/* Polargraph machine library

This contains basic code for making the machine move, getting coordinates in and out
and some universal features (straight lines, set position etc).

It relies on having motor objects injected at the object creation times so it not tied
to a particular physical implementation.
*/
#ifndef PolargraphLib
#define PolargraphLib

#include <AccelStepper.h>
#include <stdlib.h>
#include <HardwareSerial.h>
#include <Servo.h>

#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#include <wiring.h>
#endif


//  EEPROM addresses
#define EEPROM_MACHINE_WIDTH			0
#define EEPROM_MACHINE_HEIGHT			2
#define	EEPROM_MACHINE_NAME				4
#define	EEPROM_MACHINE_MM_PER_REV		14
#define	EEPROM_MACHINE_STEPS_PER_REV	16

// Default machine definition
#define DEFAULT_MACHINE_NAME 			PG01    
#define DEFAULT_MACHINE_WIDTH 			650
#define DEFAULT_MACHINE_HEIGHT			650
#define DEFAULT_MM_PER_REV				95
#define DEFAULT_STEPS_PER_REV			800

#define DIR_NE	1
#define DIR_SE	2
#define DIR_SW	3
#define DIR_NW	4

#define DIR_N	5
#define DIR_E	6
#define DIR_S	7
#define DIR_W	8

#define DIR_MODE_AUTO	1
#define DIR_MODE_PRESET	2
#define	DIR_MODE_RANDOM	3

#define PEN_HEIGHT_UP_POSITION 180
#define PEN_HEIGHT_DOWN_POSITION 90
#define PEN_HEIGHT_SERVO_PIN 10
#define PEN_HEIGHT_CHANGE_DELAY 10


class Polargraph
{
	private:
		HardwareSerial *_Serial;

		float _mmPerRev;
		float _stepsPerRev;
		float _mmPerStep;
		float _stepsPerMm;
		float _widthMm;
		float _heightMm;
		long _width;
		long _height;
		long _maxLength;
		String _name;
		AccelStepper _motorA;
		AccelStepper _motorB;
		
		bool currentlyRunning;
		bool usingAcceleration;
		bool reportingPosition;
		
		int _motorMaxSpeed;
		int _motorAcceleration;
		float _penWidth;
		
		void loadMachineSpecFromEeprom();
		void EEPROMWriteInt(int p_address, int p_value);
		unsigned int EEPROMReadInt(int p_address);
		int _globalDrawDirection;
		int _globalDrawDirectionMode;

		void useAcceleration(boolean use);
		int _availMem;
		
		// pen lift settings
		Servo _penHeightServo;
		bool _penRaised;
		void movePenUp();
		void movePenDown();

		
	public:
		// constructor
		Polargraph(AccelStepper motorA, AccelStepper motorB);

		// gondola direction
		bool lastWaveWasTop;
		bool lastMotorBiasWasA;
		int globalDrawDirection;
		int globalDrawDirectionMode;
		

		// get motors
		AccelStepper getMotorA();
		AccelStepper getMotorB();
		
		// set up machine 
		void init();
		void resetEeprom();
		void dumpEeprom();
		float getMmPerRev();
		void setMmPerRev(float size);
		float getStepsPerRev();
		void setStepsPerRev(float size);
		float getWidthMm();
		void setWidthMm(float size);
		float getHeightMm();
		void setHeightMm(float size);
		String getName();
		void setName(String nameStr);

		// basic movement functions
		void moveA(long dist);
		void moveB(long dist);
		void changeLength(long tA, long tB);
		long getMaxLength();
		void changeLengthDirect(float endA, float endB, int maxSegmentLength);
		void drawBetweenPoints(float p1a, float p1b, float p2a, float p2b, int maxSegmentLength);

		
		// Wavey stuff.  Can't think of a better description.
		void drawSquareWaveAlongA(int waveAmplitude, int waveLength, int totalWaves, int waveNo);
		void drawSquareWaveAlongB(int waveAmplitude, int waveLength, int totalWaves, int waveNo);
		void flipWaveDirection();

		int minSegmentSizeForPen(float penSize);
		int maxDensity(float penSize, int rowSize);
		int scaleDensity(int inDens, int inMax, int outMax);

		/* AS220 Drawbot */
		float rads(int n);
		void drawCurve(long x, long y, long fx, long fy, long cx, long cy);
		void drawCircleAtPosition(long centerx, long centery, int radius);

		void drawSpiralAtPosition(long centerx, long centery, int size, int brightness);
		void drawSpiral(int maxRadius, int gap, int turns);

		void drawSquarePixelAtPosition(long originA, long originB, int size, int density);
		void drawSquarePixel(int length, int width, int density, byte drawDirection);		

		void drawScribblePixelAtPosition(long originA, long originB, int pixelSize, int brightness, int swell);		
		void reportPosition();
		void outputAvailableMemory();
		void setSerial(HardwareSerial *serIn);
		unsigned int availableMemory();
		
		void setPosition(long pA, long pB);
		void engageMotors();
		void releaseMotors();

		float getMachineA(float cX, float cY);
		float getMachineB(float cX, float cY);
		float getCartesianXFP(float aPos, float bPos);
		float getCartesianYFP(float cX, float aPos);
		int getCartesianX(float aPos, float bPos);
		int getCartesianX();
		int getCartesianY();
		int getCartesianY(int cX, float aPos);

		void getCartesian(long cartesianCoords[2]);
		
		void setMotorMaxSpeed(float speed);
		float getMotorMaxSpeed();
		void setMotorAcceleration(float accel);
		float getMotorAcceleration();
		void setPenWidth(float penWidth);
		float getPenWidth();
		Servo getPenLiftServo();
		bool getPenRaised();
		void penUp();
		void penDown();
		void testPenHeight();
		void testServoRange();
		
		void testPenWidth(int rowWidth, float startWidth, float endWidth, float incSize);
		void testPenWidthScribble(int rowWidth, float startWidth, float endWidth, float incSize, float swell);
		byte getRandomDrawDirection();
		byte getAutoDrawDirection(long targetA, long targetB, long sourceA, long sourceB);
		
};

#endif
